home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1843 / 1843.xpi / content / firebug / firebug.js < prev    next >
Text File  |  2010-01-15  |  86KB  |  2,939 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. FBL.ns(function() { with (FBL) {
  4.  
  5. // ************************************************************************************************
  6. // Constants
  7.  
  8. const Cc = Components.classes;
  9. const Ci = Components.interfaces;
  10.  
  11. const nsIPrefBranch = Ci.nsIPrefBranch;
  12. const nsIPrefBranch2 = Ci.nsIPrefBranch2;
  13. const nsISupports = Ci.nsISupports;
  14. const nsIFile = Ci.nsIFile;
  15. const nsILocalFile = Ci.nsILocalFile;
  16. const nsISafeOutputStream = Ci.nsISafeOutputStream;
  17. const nsIURI = Ci.nsIURI;
  18.  
  19. const PrefService = Cc["@mozilla.org/preferences-service;1"];
  20. const DirService =  CCSV("@mozilla.org/file/directory_service;1", "nsIDirectoryServiceProvider");
  21.  
  22. const nsIPrefService = Ci.nsIPrefService;
  23. const prefService = PrefService.getService(nsIPrefService);
  24.  
  25. const observerService = CCSV("@mozilla.org/observer-service;1", "nsIObserverService");
  26. const categoryManager = CCSV("@mozilla.org/categorymanager;1", "nsICategoryManager");
  27. const stringBundleService = CCSV("@mozilla.org/intl/stringbundle;1", "nsIStringBundleService");
  28. const promptService = CCSV("@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService");
  29.  
  30. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  31. // There is one Firebug object per browser.xul
  32.  
  33. const contentBox = $("fbContentBox");
  34. const contentSplitter = $("fbContentSplitter");
  35. const toggleCommand = $("cmd_toggleFirebug");
  36. const detachCommand = $("cmd_toggleDetachFirebug");
  37. const tabBrowser = $("content");
  38. const versionURL = "chrome://firebug/content/branch.properties";
  39. const statusBarContextMenu = $("fbStatusContextMenu");
  40.  
  41. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  42.  
  43. const prefs = PrefService.getService(nsIPrefBranch2);
  44. const NS_OS_TEMP_DIR = "TmpD"
  45.  
  46. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  47.  
  48. const firebugURLs =
  49. {
  50.     main: "http://www.getfirebug.com",
  51.     docs: "http://www.getfirebug.com/docs.html",
  52.     keyboard: "http://www.getfirebug.com/keyboard.html",
  53.     discuss: "http://groups.google.com/group/firebug",
  54.     issues: "http://code.google.com/p/fbug/issues/list",
  55.     donate: "http://www.getfirebug.com/contribute.html?product"
  56. };
  57.  
  58. const prefNames =  // XXXjjb TODO distribute to modules
  59. [
  60.     // Global
  61.     "defaultPanelName", "throttleMessages", "textSize", "showInfoTips",
  62.     "largeCommandLine", "textWrapWidth", "openInWindow", "showErrorCount",
  63.     "activateSameOrigin", "allPagesActivation",
  64.  
  65.     // Search
  66.     "searchCaseSensitive", "searchGlobal", "netSearchHeaders", "netSearchParameters",
  67.     "netSearchResponseBody",
  68.  
  69.     // Console
  70.     "showJSErrors", "showJSWarnings", "showCSSErrors", "showXMLErrors",
  71.     "showChromeErrors", "showChromeMessages", "showExternalErrors",
  72.     "showXMLHttpRequests", "showNetworkErrors",
  73.  
  74.     "persistBreakOnError",
  75.  
  76.     // HTML
  77.     "showFullTextNodes", "showCommentNodes",
  78.     "showTextNodesWithWhitespace", "showTextNodesWithEntities",
  79.     "highlightMutations", "expandMutations", "scrollToMutations", "shadeBoxModel",
  80.     "showQuickInfoBox",
  81.  
  82.     // CSS
  83.     "showUserAgentCSS",
  84.     "expandShorthandProps",
  85.  
  86.     // Script
  87.     "decompileEvals", "replaceTabs",
  88.  
  89.     // DOM
  90.     "showUserProps", "showUserFuncs", "showDOMProps", "showDOMFuncs", "showDOMConstants",
  91.  
  92.     // Layout
  93.     "showRulers",
  94.  
  95.     // Net
  96.     "netFilterCategory", "collectHttpHeaders", "netDisplayedResponseLimit",
  97.     "netDisplayedPostBodyLimit", "netPhaseInterval", "sizePrecision",
  98.  
  99.     // Stack
  100.     "omitObjectPathStack",
  101.  
  102.     // Debugging
  103.     "clearDomplate"
  104. ];
  105.  
  106. const servicePrefNames = [
  107.     "showStackTrace", // Console
  108.     "filterSystemURLs", // Stack
  109.     "showAllSourceFiles", "breakOnErrors",  "trackThrowCatch" // Script
  110. ];
  111.  
  112. const scriptBlockSize = 20;
  113.  
  114. const PLACEMENT_NONE = 0;
  115. const PLACEMENT_INBROWSER = 1;
  116. const PLACEMENT_DETACHED = 2;
  117. const PLACEMENT_MINIMIZED = 3;
  118.  
  119. // ************************************************************************************************
  120. // Globals
  121.  
  122. var modules = [];
  123. var activeContexts = [];
  124. var activableModules = [];
  125. var extensions = [];
  126. var panelTypes = [];
  127. var reps = [];
  128. var defaultRep = null;
  129. var defaultFuncRep = null;
  130. var editors = [];
  131. var externalEditors = [];
  132.  
  133. var panelTypeMap = {};
  134. var optionUpdateMap = {};
  135.  
  136. var deadWindows = [];
  137. var deadWindowTimeout = 0;
  138. var clearContextTimeout = 0;
  139. var temporaryFiles = [];
  140. var temporaryDirectory = null;
  141.  
  142. // Register default Firebug string bundle (yet before domplate templates).
  143. categoryManager.addCategoryEntry("strings_firebug",
  144.     "chrome://firebug/locale/firebug.properties", "", true, true);
  145.  
  146. // ************************************************************************************************
  147.  
  148. /**
  149.  * @class Represents the main Firebug application object. An instance of this object is
  150.  * created for each browser window (browser.xul).
  151.  */
  152. top.Firebug =
  153. {
  154.     version: "1.5",
  155.  
  156.     dispatchName: "Firebug",
  157.     module: modules,
  158.     panelTypes: panelTypes,
  159.     uiListeners: [],
  160.     reps: reps,
  161.     prefDomain: "extensions.firebug",
  162.     servicePrefDomain: "extensions.firebug.service",
  163.  
  164.     stringCropLength: 50,
  165.  
  166.     tabBrowser: tabBrowser,
  167.     originalChrome: FirebugChrome,
  168.     chrome: FirebugChrome,
  169.  
  170.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  171.     // Initialization
  172.  
  173.     initialize: function()
  174.     {
  175.         var version = this.getVersion();
  176.         if (version)
  177.         {
  178.             this.version = version;
  179.             $('fbStatusBar').setAttribute("tooltiptext", "Firebug " + version);
  180.  
  181.             var about = $('Firebug_About');
  182.             if (about)
  183.             {
  184.                 var aboutLabel = about.getAttribute("label");
  185.                 $('Firebug_About').setAttribute("label",  aboutLabel + " " + version);
  186.             }
  187.         }
  188.  
  189.         for (var i = 0; i < prefNames.length; ++i)
  190.             this[prefNames[i]] = this.getPref(this.prefDomain, prefNames[i]);
  191.         for (var i = 0; i < servicePrefNames.length; ++i)
  192.             this[servicePrefNames[i]] = this.getPref(this.servicePrefDomain, servicePrefNames[i]);
  193.  
  194.         this.loadExternalEditors();
  195.  
  196.         prefs.addObserver(this.prefDomain, this, false);
  197.         prefs.addObserver(this.servicePrefDomain, this, false);
  198.  
  199.         var basePrefNames = prefNames.length;
  200.  
  201.         this.clientID = Firebug.Debugger.registerClient(this);
  202.  
  203.         dispatch(modules, "initialize", [this.prefDomain, prefNames]);
  204.  
  205.         for (var i = basePrefNames; i < prefNames.length; ++i)
  206.             this[prefNames[i]] = this.getPref(this.prefDomain, prefNames[i]);
  207.  
  208.         window.addEventListener('unload', shutdownFirebug, false);
  209.     },
  210.  
  211.     getVersion: function()
  212.     {
  213.         if (!this.fullVersion)
  214.             this.fullVersion = this.loadVersion(versionURL);
  215.  
  216.         return this.fullVersion;
  217.     },
  218.  
  219.     loadVersion: function(versionURL)
  220.     {
  221.         var content = getResource(versionURL);
  222.         if (!content)
  223.             return "no content at "+versionURL;
  224.  
  225.         var m = /RELEASE=(.*)/.exec(content);
  226.         if (m)
  227.             var release = m[1];
  228.         else
  229.             return "no RELEASE in "+versionURL;
  230.  
  231.         m = /VERSION=(.*)/.exec(content);
  232.         if (m)
  233.             var version = m[1];
  234.         else
  235.             return "no VERSION in "+versionURL;
  236.  
  237.         return version+""+release;
  238.     },
  239.  
  240.     internationalizeUI: function(doc)  // Substitute strings in the UI with fall back to en-US
  241.     {
  242.         if (!doc)
  243.             return;
  244.  
  245.         var elements = ["menu_clearConsole", "menu_resetAllOptions",
  246.             "menu_enablePanels", "menu_disablePanels",
  247.             "fbCommandLine", "fbFirebugMenu", "fbLargeCommandLine", "menu_customizeShortcuts",
  248.             "menu_enableA11y", "menu_activateSameOrigin", "menu_onByDefault", "fbContinueButton",
  249.             "fbBreakOnNextButton", "fbConsolePersist",
  250.             "fbMinimizeButton", "FirebugMenu_Sites", "fbResumeBoxButton",
  251.             "menu_AllOn", "menu_clearActivationList", "showQuickInfoBox"];
  252.  
  253.         for (var i=0; i<elements.length; i++)
  254.         {
  255.             var element = doc.getElementById(elements[i]);
  256.             if (!element)
  257.             {
  258.                 continue;
  259.             }
  260.  
  261.             if (element.hasAttribute("label"))
  262.                 FBL.internationalize(element, "label");
  263.  
  264.             if (element.hasAttribute("tooltiptext"))
  265.                 FBL.internationalize(element, "tooltiptext");
  266.         }
  267.  
  268.         // Allow other modules to internationalize UI labels (called also for
  269.         // detached Firebug window).
  270.         dispatch(modules, "internationalizeUI", [doc]);
  271.     },
  272.  
  273.     broadcast: function(message, args)
  274.     {
  275.         // dispatch message to all XUL windows registered to firebug service.
  276.         // Implemented in Firebug.Debugger.
  277.     },
  278.  
  279.     /**
  280.      * Called when the UI is ready to be initialized, once the panel browsers are loaded,
  281.      * but before any contexts are created.
  282.      */
  283.     initializeUI: function(detachArgs)
  284.     {
  285.         TabWatcher.initialize(this);
  286.         TabWatcher.addListener(this);
  287.  
  288.         // Initialize all modules.
  289.         dispatch(modules, "initializeUI", [detachArgs]);
  290.     },
  291.  
  292.  
  293.     shutdown: function()  // called in browser when Firefox closes and in externalMode when fbs gets quitApplicationGranted.
  294.     {
  295.         window.removeEventListener('unload', shutdownFirebug, false);
  296.  
  297.         Firebug.Debugger.unregisterClient(this);
  298.  
  299.         TabWatcher.destroy();
  300.  
  301.         // Remove the listener after the TabWatcher.destroy() method is called so,
  302.         // destroyContext event is properly dispatched to the Firebug object and
  303.         // consequently to all registered modules.
  304.         TabWatcher.removeListener(this);
  305.  
  306.         dispatch(modules, "disable", [FirebugChrome]);
  307.  
  308.         prefService.savePrefFile(null);
  309.         prefs.removeObserver(this.prefDomain, this, false);
  310.         prefs.removeObserver(this.servicePrefDomain, this, false);
  311.  
  312.         dispatch(modules, "shutdown");
  313.  
  314.         this.closeDeadWindows();
  315.         this.deleteTemporaryFiles();
  316.  
  317.     },
  318.  
  319.     // ----------------------------------------------------------------------------------------------------------------
  320.  
  321.     getSuspended: function()
  322.     {
  323.         var suspendMarker = $("fbStatusIcon");
  324.         if (suspendMarker.hasAttribute("suspended"))
  325.             return suspendMarker.getAttribute("suspended");
  326.         return null;
  327.     },
  328.  
  329.     setSuspended: function(value)
  330.     {
  331.         var suspendMarker = $("fbStatusIcon");
  332.         if (value)
  333.             suspendMarker.setAttribute("suspended", value);
  334.         else
  335.             suspendMarker.removeAttribute("suspended");
  336.  
  337.         Firebug.resetTooltip();
  338.     },
  339.  
  340.     toggleSuspend: function()
  341.     {
  342.         // getSuspended returns non-null value if Firebug is suspended.
  343.         if (this.getSuspended())
  344.         {
  345.             // Firebug is suspended now. Two possible actions have been executed:
  346.             // 1) Firebug UI is closed and the user clicked on the status bar icon in order to
  347.             //    show the UI and resume Firebug.
  348.             // 2) Firebug is detached, but suspended for the current page. The user clicked
  349.             //    either on the status bar icon or on an activation button that is displayed
  350.             //    within detached Firebug window.
  351.             this.toggleBar(true);
  352.         }
  353.         else
  354.         {
  355.             // The users wants to suspend Firebug, let's do it and pull down the visible UI.
  356.             // xxxHonza: the Firebug isn't suspended if detached and the user clicks on the
  357.             // status bar icon (the detached window should becoma blank displaying only
  358.             // the activation button).
  359.             this.suspend();
  360.  
  361.             // Close detached Firebug or
  362.             // show/hide Firebug UI according to the browser.showFirebug flag.
  363.             if (Firebug.isDetached())
  364.                 this.toggleDetachBar(false);
  365.             else
  366.                 this.syncBar();
  367.         }
  368.     },
  369.  
  370.     disablePanels: function(context)
  371.     {
  372.         Firebug.ModuleManager.disableModules();
  373.     },
  374.  
  375.     suspend: function()  // dispatch suspendFirebug to all windows
  376.     {
  377.         this.broadcast('suspendFirebug', []);
  378.     },
  379.  
  380.     suspendFirebug: function() // dispatch onSuspendFirebug to all modules
  381.     {
  382.         this.setSuspended("suspending");
  383.  
  384.         var cancelSuspend = dispatch2(activableModules, 'onSuspendFirebug', [FirebugContext]);  // TODO no context arg
  385.  
  386.         if (cancelSuspend)
  387.             Firebug.resume();
  388.         else
  389.             this.setSuspended("suspended");
  390.     },
  391.  
  392.     resume: function()
  393.     {
  394.         this.broadcast('resumeFirebug', []);
  395.     },
  396.  
  397.     resumeFirebug: function()  // dispatch onResumeFirebug to all modules
  398.     {
  399.         this.setSuspended("resuming");
  400.         dispatch(activableModules, 'onResumeFirebug', [FirebugContext]);// TODO no context arg
  401.         this.setSuspended(null);
  402.     },
  403.  
  404.     getEnablementStatus: function()
  405.     {
  406.         var strOn = $STR("enablement.on");
  407.         var strOff = $STR("enablement.off");
  408.  
  409.         var status = "";
  410.         var fbStatusIcon = $('fbStatusIcon');
  411.         if (fbStatusIcon.getAttribute("console") == "on")
  412.             status +="Console: "+strOn+",";
  413.         else
  414.             status +="Console: "+strOff+",";
  415.  
  416.         if (fbStatusIcon.getAttribute("net") == "on")
  417.             status +=" Net: "+strOn+",";
  418.         else
  419.             status +=" Net: "+strOff+",";
  420.  
  421.         if (fbStatusIcon.getAttribute("script") == "on")
  422.             status +=" Script: "+strOn;
  423.         else
  424.             status +=" Script: "+strOff+"";
  425.  
  426.         return status;
  427.     },
  428.  
  429.     resetTooltip: function()
  430.     {
  431.         var tooltip = "Firebug " + Firebug.getVersion();
  432.  
  433.         tooltip += "\n" + Firebug.getEnablementStatus();
  434.  
  435.         if (Firebug.getSuspended())
  436.             tooltip += "\n" + Firebug.getSuspended();
  437.         else
  438.             tooltip += "\n" + $STRP("plural.Total_Firebugs", [TabWatcher.contexts.length]);
  439.  
  440.         if (Firebug.allPagesActivation == "on")
  441.         {
  442.             var label = $STR("enablement.on");
  443.             tooltip += "\n"+label+" "+$STR("enablement.for all pages");
  444.         }
  445.         // else allPagesActivation == "none" we don't show it.
  446.  
  447.         tooltip += "\n" + $STR(Firebug.getPlacement());
  448.  
  449.         $('fbStatusBar').setAttribute("tooltiptext", tooltip);
  450.     },
  451.  
  452.     getURLsForAllActiveContexts: function()
  453.     {
  454.         var contextURLSet = [];  // create a list of all unique activeContexts
  455.         TabWatcher.iterateContexts( function createActiveContextList(context)
  456.         {
  457.             try
  458.             {
  459.                 var cw = context.window;
  460.                 if (cw)
  461.                 {
  462.                     if (cw.closed)
  463.                         url = "about:closed";
  464.                     else
  465.                         if ('location' in cw)
  466.                             var url = cw.location.toString();
  467.                         else
  468.                             var url = context.getName();
  469.                     if (url)
  470.                     {
  471.                         if (contextURLSet.indexOf(url) == -1)
  472.                             contextURLSet.push(url);
  473.                     }
  474.                 }
  475.             }
  476.             catch(e)
  477.             {
  478.             }
  479.         });
  480.  
  481.         return contextURLSet;
  482.     },
  483.  
  484.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  485.     // Dead Windows  XXXjjb this code is not used by 1.4, external placement.
  486.  
  487.     killWindow: function(browser, chrome)
  488.     {
  489.         deadWindows.push({browser: browser, chrome: chrome});
  490.         deadWindowTimeout = setTimeout(function() { Firebug.closeDeadWindows(); }, 3000);
  491.     },
  492.  
  493.     rescueWindow: function(browser)
  494.     {
  495.         for (var i = 0; i < deadWindows.length; ++i)
  496.         {
  497.             if (deadWindows[i].browser == browser)
  498.             {
  499.                 deadWindows.splice(i, 1);
  500.                 break;
  501.             }
  502.         }
  503.     },
  504.  
  505.     closeDeadWindows: function()
  506.     {
  507.         for (var i = 0; i < deadWindows.length; ++i)
  508.             deadWindows[i].chrome.close();
  509.  
  510.         deadWindows = [];
  511.         deadWindowTimeout = 0;
  512.     },
  513.  
  514.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  515.     // Registration
  516.  
  517.     registerModule: function()
  518.     {
  519.         modules.push.apply(modules, arguments);
  520.  
  521.     },
  522.  
  523.     unregisterModule: function()
  524.     {
  525.         for (var i = 0; i < arguments.length; ++i)
  526.             remove(modules, arguments[i]);
  527.     },
  528.  
  529.     registerActivableModule: function()
  530.     {
  531.         activableModules.push.apply(activableModules, arguments);
  532.         this.registerModule.apply(this, arguments);
  533.     },
  534.  
  535.     registerExtension: function()  // TODO remove
  536.     {
  537.         extensions.push.apply(extensions, arguments);
  538.  
  539.         for (var i = 0; i < arguments.length; ++i)
  540.             TabWatcher.addListener(arguments[i]);
  541.  
  542.         for (var j = 0; j < arguments.length; j++)
  543.             Firebug.uiListeners.push(arguments[j]);
  544.     },
  545.  
  546.     unregisterExtension: function()  // TODO remove
  547.     {
  548.         for (var i = 0; i < arguments.length; ++i)
  549.         {
  550.             TabWatcher.removeListener(arguments[i]);
  551.             remove(Firebug.uiListeners, arguments[i]);
  552.             remove(extensions, arguments[i])
  553.         }
  554.     },
  555.  
  556.     registerUIListener: function()
  557.     {
  558.         for (var j = 0; j < arguments.length; j++)
  559.             Firebug.uiListeners.push(arguments[j]);
  560.     },
  561.  
  562.     unregisterUIListener: function()
  563.     {
  564.         for (var i = 0; i < arguments.length; ++i)
  565.             remove(Firebug.uiListeners, arguments[i]);
  566.     },
  567.  
  568.     registerPanel: function()
  569.     {
  570.         panelTypes.push.apply(panelTypes, arguments);
  571.  
  572.         for (var i = 0; i < arguments.length; ++i)
  573.             panelTypeMap[arguments[i].prototype.name] = arguments[i];
  574.  
  575.     },
  576.  
  577.     registerRep: function()
  578.     {
  579.         reps.push.apply(reps, arguments);
  580.     },
  581.  
  582.     unregisterRep: function()
  583.     {
  584.         for (var i = 0; i < arguments.length; ++i)
  585.             remove(reps, arguments[i]);
  586.     },
  587.  
  588.     setDefaultReps: function(funcRep, rep)
  589.     {
  590.         defaultRep = rep;
  591.         defaultFuncRep = funcRep;
  592.     },
  593.  
  594.     registerEditor: function()
  595.     {
  596.         editors.push.apply(editors, arguments);
  597.     },
  598.  
  599.     registerStringBundle: function(bundleURI)
  600.     {
  601.         categoryManager.addCategoryEntry("strings_firebug", bundleURI, "", true, true);
  602.         this.stringBundle = null;
  603.     },
  604.  
  605.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  606.     // Localization
  607.  
  608.     getStringBundle: function()
  609.     {
  610.         if (!this.stringBundle)
  611.             this.stringBundle = stringBundleService.createExtensibleBundle("strings_firebug");
  612.         return this.stringBundle;
  613.     },
  614.  
  615.     getPluralRule: function()
  616.     {
  617.         try {
  618.             return this.getStringBundle().GetStringFromName("pluralRule");
  619.         } catch (err) { }
  620.     },
  621.  
  622.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  623.     // Options
  624.  
  625.     togglePref: function(name)
  626.     {
  627.         this.setPref(Firebug.prefDomain, name, !this[name]);
  628.     },
  629.  
  630.     getPref: function(prefDomain, name)
  631.     {
  632.         var prefName = prefDomain + "." + name;
  633.  
  634.         var type = prefs.getPrefType(prefName);
  635.         if (type == nsIPrefBranch.PREF_STRING)
  636.             return prefs.getCharPref(prefName);
  637.         else if (type == nsIPrefBranch.PREF_INT)
  638.             return prefs.getIntPref(prefName);
  639.         else if (type == nsIPrefBranch.PREF_BOOL)
  640.             return prefs.getBoolPref(prefName);
  641.     },
  642.  
  643.     setPref: function(prefDomain, name, value)
  644.     {
  645.         var prefName = prefDomain + "." + name;
  646.  
  647.         var type = prefs.getPrefType(prefName);
  648.         if (type == nsIPrefBranch.PREF_STRING)
  649.             prefs.setCharPref(prefName, value);
  650.         else if (type == nsIPrefBranch.PREF_INT)
  651.             prefs.setIntPref(prefName, value);
  652.         else if (type == nsIPrefBranch.PREF_BOOL)
  653.             prefs.setBoolPref(prefName, value);
  654.         else if (type == nsIPrefBranch.PREF_INVALID)
  655.         {
  656.             FBTrace.sysout("firebug.setPref FAILS: Invalid preference "+prefName+" check that it is listed in defaults/prefs.js");
  657.         }
  658.  
  659.     },
  660.  
  661.     clearPref: function(prefDomain, name)
  662.     {
  663.         var prefName = prefDomain + "." + name;
  664.         if (prefs.prefHasUserValue(prefName))
  665.             prefs.clearUserPref(prefName);
  666.     },
  667.  
  668.     increaseTextSize: function(amt)
  669.     {
  670.         this.setTextSize(this.textSize+amt);
  671.     },
  672.  
  673.     setTextSize: function(value)
  674.     {
  675.         this.setPref(Firebug.prefDomain, "textSize", value);
  676.     },
  677.  
  678.     updatePref: function(name, value)
  679.     {
  680.         // Prevent infinite recursion due to pref observer
  681.         if ( optionUpdateMap.hasOwnProperty(name) )
  682.             return;
  683.  
  684.         optionUpdateMap[name] = 1;
  685.         this[name] = value;
  686.  
  687.         dispatch(modules, "updateOption", [name, value]);
  688.  
  689.         // Update the current chrome...
  690.         Firebug.chrome.updateOption(name, value);
  691.  
  692.         // ... as well as the original in-browser chrome (if Firebug is currently detached).
  693.         // xxxHonza, xxxJJB: John, the Firebug.externalChrome is not longer set, is it correct?
  694.         // it's still used in FirebugChrome.setGlobalAttribute.
  695.         if (Firebug.chrome != Firebug.originalChrome)
  696.             Firebug.originalChrome.updateOption(name, value);
  697.  
  698.         if (name.substr(0, 15) == "externalEditors")
  699.             this.loadExternalEditors();
  700.  
  701.         delete optionUpdateMap[name];
  702.  
  703.     },
  704.  
  705.     // ********************************************************************************************
  706.     // External editors
  707.     // TODO move to editors.js as Firebug.Editors module
  708.  
  709.     loadExternalEditors: function()
  710.     {
  711.         const prefName = "externalEditors";
  712.         const editorPrefNames = ["label", "executable", "cmdline", "image"];
  713.  
  714.         externalEditors = [];
  715.         var list = this.getPref(this.prefDomain, prefName).split(",");
  716.         for (var i = 0; i < list.length; ++i)
  717.         {
  718.             var editorId = list[i];
  719.             if ( !editorId || editorId == "")
  720.                 continue;
  721.             var item = { id: editorId };
  722.             for( var j = 0; j < editorPrefNames.length; ++j )
  723.             {
  724.                 try {
  725.                     item[editorPrefNames[j]] = this.getPref(this.prefDomain, prefName+"."+editorId+"."+editorPrefNames[j]);
  726.                 }
  727.                 catch(exc)
  728.                 {
  729.                 }
  730.             }
  731.             if ( item.label && item.executable )
  732.             {
  733.                 if (!item.image)
  734.                     item.image = getIconURLForFile(item.executable);
  735.                 externalEditors.push(item);
  736.             }
  737.         }
  738.         return externalEditors;
  739.     },
  740.  
  741.     get registeredEditors()
  742.     {
  743.         var newArray = [];
  744.         if ( editors.length > 0 )
  745.         {
  746.             newArray.push.apply(newArray, editors);
  747.             if ( externalEditors.length > 0 )
  748.                 newArray.push("-");
  749.         }
  750.         if ( externalEditors.length > 0 )
  751.             newArray.push.apply(newArray, externalEditors);
  752.  
  753.         return newArray;
  754.     },
  755.  
  756.     openEditors: function()
  757.     {
  758.         var args = {
  759.             FBL: FBL,
  760.             prefName: this.prefDomain + ".externalEditors"
  761.         };
  762.         openWindow("Firebug:ExternalEditors", "chrome://firebug/content/editors.xul", "", args);
  763.     },
  764.  
  765.     openInEditor: function(context, editorId)
  766.     {
  767.         try
  768.         {
  769.             if (!editorId)
  770.                 return;
  771.  
  772.             var location;
  773.             if (context)
  774.             {
  775.                 var panel = Firebug.chrome.getSelectedPanel();
  776.                 if (panel)
  777.                 {
  778.                     location = panel.location;
  779.                     if (!location && panel.name == "html")
  780.                         location = context.window.document.location;
  781.                     if (location && (location instanceof Firebug.SourceFile || location instanceof CSSStyleSheet ))
  782.                         location = location.href;
  783.                 }
  784.             }
  785.             if (!location)
  786.             {
  787.                 if (tabBrowser.currentURI)
  788.                     location = tabBrowser.currentURI.asciiSpec;
  789.             }
  790.             if (!location)
  791.                 return;
  792.             location = location.href || location.toString();
  793.             if (Firebug.filterSystemURLs && isSystemURL(location))
  794.                 return;
  795.  
  796.             var list = extendArray(editors, externalEditors);
  797.             var editor = null;
  798.             for( var i = 0; i < list.length; ++i )
  799.             {
  800.                 if (editorId == list[i].id)
  801.                 {
  802.                     editor = list[i];
  803.                     break;
  804.                 }
  805.             }
  806.             if (editor)
  807.             {
  808.                 if (editor.handler)
  809.                 {
  810.                     editor.handler(location);
  811.                     return;
  812.                 }
  813.                 var args = [];
  814.                 var localFile = null;
  815.                 var targetAdded = false;
  816.                 if (editor.cmdline)
  817.                 {
  818.                     args = editor.cmdline.split(" ");
  819.                     for( var i = 0; i < args.length; ++i )
  820.                     {
  821.                         if ( args[i] == "%url" )
  822.                         {
  823.                             args[i] = location;
  824.                             targetAdded = true;
  825.                         }
  826.                         else if ( args[i] == "%file" )
  827.                         {
  828.                             if (!localFile)
  829.                                 localFile = this.getLocalSourceFile(context, location);
  830.                             args[i] = localFile;
  831.                             targetAdded = true;
  832.                         }
  833.                     }
  834.                 }
  835.                 if (!targetAdded)
  836.                 {
  837.                     localFile = this.getLocalSourceFile(context, location);
  838.                     if (!localFile)
  839.                         return;
  840.                     args.push(localFile);
  841.                 }
  842.                 FBL.launchProgram(editor.executable, args);
  843.             }
  844.         } catch(exc) { ERROR(exc); }
  845.     },
  846.  
  847.     getLocalSourceFile: function(context, href)
  848.     {
  849.         if ( isLocalURL(href) )
  850.             return getLocalPath(href);
  851.  
  852.         var data;
  853.         if (context)
  854.         {
  855.             data = context.sourceCache.loadText(href);
  856.         }
  857.         else
  858.         {
  859.             // xxxHonza: if the fake context is used the source code is always get using
  860.             // (a) the browser cache or (b) request to the server.
  861.             var selectedBrowser = Firebug.chrome.getCurrentBrowser();
  862.             var ctx = {
  863.                 browser: selectedBrowser,
  864.                 window: selectedBrowser.contentWindow
  865.             };
  866.             data = new Firebug.SourceCache(ctx).loadText(href);
  867.         }
  868.  
  869.         if (!data)
  870.             return;
  871.  
  872.         if (!temporaryDirectory)
  873.         {
  874.             var tmpDir = DirService.getFile(NS_OS_TEMP_DIR, {});
  875.             tmpDir.append("fbtmp");
  876.             tmpDir.createUnique(nsIFile.DIRECTORY_TYPE, 0775);
  877.             temporaryDirectory = tmpDir;
  878.         }
  879.  
  880.         var lpath = href.replace(/^[^:]+:\/*/g, "").replace(/\?.*$/g, "").replace(/[^0-9a-zA-Z\/.]/g, "_");
  881.         /* dummy comment to workaround eclipse bug */
  882.         if ( !/\.[\w]{1,5}$/.test(lpath) )
  883.         {
  884.             if ( lpath.charAt(lpath.length-1) == '/' )
  885.                 lpath += "index";
  886.             lpath += ".html";
  887.         }
  888.  
  889.         if ( getPlatformName() == "WINNT" )
  890.             lpath = lpath.replace(/\//g, "\\");
  891.  
  892.         var file = QI(temporaryDirectory.clone(), nsILocalFile);
  893.         file.appendRelativePath(lpath);
  894.         if (!file.exists())
  895.             file.create(nsIFile.NORMAL_FILE_TYPE, 0664);
  896.         temporaryFiles.push(file.path);
  897.  
  898.         var converter = CCIN("@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter");
  899.         converter.charset = 'UTF-8'; // TODO detect charset from current tab
  900.         data = converter.ConvertFromUnicode(data);
  901.  
  902.         var stream = CCIN("@mozilla.org/network/safe-file-output-stream;1", "nsIFileOutputStream");
  903.         stream.init(file, 0x04 | 0x08 | 0x20, 0664, 0); // write, create, truncate
  904.         stream.write(data, data.length);
  905.         if (stream instanceof nsISafeOutputStream)
  906.             stream.finish();
  907.         else
  908.             stream.close();
  909.  
  910.         return file.path;
  911.     },
  912.  
  913.     deleteTemporaryFiles: function()  // TODO call on "shutdown" event to modules
  914.     {
  915.         try {
  916.             var file = CCIN("@mozilla.org/file/local;1", "nsILocalFile");
  917.             for( var i = 0; i < temporaryFiles.length; ++i)
  918.             {
  919.                 file.initWithPath(temporaryFiles[i]);
  920.                 if (file.exists())
  921.                     file.remove(false);
  922.             }
  923.         }
  924.         catch(exc)
  925.         {
  926.         }
  927.         try {
  928.             if (temporaryDirectory && temporaryDirectory.exists())
  929.                 temporaryDirectory.remove(true);
  930.         } catch(exc)
  931.         {
  932.         }
  933.     },
  934.  
  935.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  936.     // Browser Bottom Bar
  937.  
  938.     showBar: function(show)
  939.     {
  940.         var browser = Firebug.chrome.getCurrentBrowser();
  941.         var contentBox = Firebug.chrome.$("fbContentBox");
  942.         var contentSplitter = Firebug.chrome.$("fbContentSplitter");
  943.  
  944.         var shouldShow = show/* && !Firebug.isDetached()*/;
  945.         contentBox.setAttribute("collapsed", !shouldShow);
  946.  
  947.         if(!show)
  948.             Firebug.Inspector.inspectNode(null);
  949.  
  950.         if (contentSplitter)
  951.             contentSplitter.setAttribute("collapsed", !shouldShow);
  952.  
  953.         if (toggleCommand)
  954.             toggleCommand.setAttribute("checked", !!shouldShow);
  955.  
  956.         if (detachCommand)
  957.             detachCommand.setAttribute("checked", Firebug.isDetached());
  958.  
  959.         this.showKeys(shouldShow);
  960.  
  961.         dispatch(Firebug.uiListeners, show ? "showUI" : "hideUI", [browser, FirebugContext]);
  962.  
  963.         // Sync panel state after the showUI event is dispatched. syncPanel method calls
  964.         // Panel.show method, which expects the active context to be already registered.
  965.         if (show)
  966.             Firebug.chrome.syncPanel();
  967.     },
  968.  
  969.     showKeys: function(shouldShow)
  970.     {
  971.         if (!this.fbOnlyKeys)
  972.         {
  973.             var keyset = document.getElementById("mainKeyset");
  974.             this.fbOnlyKeys = keyset.getElementsByClassName("fbOnlyKey").item(0);
  975.         }
  976.         var keys = this.fbOnlyKeys;
  977.         for (var i = 0; i < keys.length; i++)
  978.             keys[i].setAttribute("disabled", !!shouldShow);
  979.     },
  980.  
  981.     closeFirebug: function(userCommand)  // this is really deactivate
  982.     {
  983.         var browser = FirebugChrome.getCurrentBrowser();
  984.  
  985.         TabWatcher.unwatchBrowser(browser, userCommand);
  986.         Firebug.resetTooltip();
  987.     },
  988.  
  989.     /*
  990.      * Primary function to activate or minimize firebug. Used by
  991.      * <ol>
  992.      * <li>the status bar icon click action</li>
  993.      * <li>the activation button (within Firebug.xul) click action</li>
  994.      * </ol>
  995.      * @param forceOpen: don't minimize, stay open if open.
  996.      * @param panelName: eg 'script', to select a specific panel.
  997.      */
  998.     toggleBar: function(forceOpen, panelName)
  999.     {
  1000.         var browser = FirebugChrome.getCurrentBrowser();
  1001.  
  1002.         if (panelName)
  1003.             Firebug.chrome.selectPanel(panelName);
  1004.  
  1005.         if (FirebugContext && browser.showFirebug)  // then we are already debugging the selected tab
  1006.         {
  1007.             if (Firebug.isDetached()) // if we are out of the browser focus the window
  1008.                 Firebug.chrome.focus();
  1009.             else if (Firebug.openInWindow)
  1010.                 this.detachBar(context);
  1011.             else if (Firebug.isMinimized()) // toggle minimize
  1012.                 Firebug.unMinimize();
  1013.             else if (!forceOpen)  // else isInBrowser
  1014.                 Firebug.minimizeBar();
  1015.         }
  1016.         else  // closed or no context or no showFirebug
  1017.         {
  1018.             var created = TabWatcher.watchBrowser(browser);  // create a context for this page
  1019.             if (!created)
  1020.             {
  1021.                 return false;
  1022.             }
  1023.  
  1024.             if (Firebug.isMinimized()) // then toggle minimize
  1025.                 Firebug.unMinimize();
  1026.         }
  1027.         return true;
  1028.      },
  1029.  
  1030.     minimizeBar: function()  // just pull down the UI, but don't deactivate the context
  1031.     {
  1032.         if (Firebug.isDetached())  // TODO disable minimize on externalMode
  1033.         {
  1034.             // TODO reattach
  1035.             Firebug.toggleDetachBar(false, false);
  1036.             Firebug.chrome.focus() ;
  1037.         }
  1038.         else // inBrowser -> minimized
  1039.         {
  1040.             Firebug.setPlacement("minimized");
  1041.             this.showBar(false);
  1042.         }
  1043.     },
  1044.  
  1045.     unMinimize: function()
  1046.     {
  1047.         this.updateActiveContexts(FirebugContext);
  1048.         Firebug.setPlacement("inBrowser");
  1049.         Firebug.showBar(true);
  1050.     },
  1051.  
  1052.     toggleDetachBar: function(forceOpen, reopenInBrowser)  // detached -> closed; inBrowser -> detached TODO reattach
  1053.     {
  1054.         if (!forceOpen && Firebug.isDetached())  // detached -> minimized
  1055.         {
  1056.             Firebug.chrome.close();
  1057.             detachCommand.setAttribute("checked", false);
  1058.             if (reopenInBrowser)
  1059.             {
  1060.                 setTimeout(function delayMinimize()
  1061.                 {
  1062.                     Firebug.unMinimize()
  1063.                 });
  1064.             }
  1065.         }
  1066.         else
  1067.             this.detachBar(FirebugContext);
  1068.     },
  1069.  
  1070.     closeDetachedWindow: function(userCommands)
  1071.     {
  1072.         Firebug.showBar(false);
  1073.  
  1074.         if (FirebugContext)
  1075.             TabWatcher.unwatchBrowser(FirebugContext.browser, userCommands);
  1076.         // else the user closed Firebug external window while not looking at a debugged web page.
  1077.  
  1078.         Firebug.resetTooltip();
  1079.     },
  1080.  
  1081.     setChrome: function(newChrome, newPlacement)
  1082.     {
  1083.         var oldChrome = Firebug.chrome;
  1084.         Firebug.chrome = newChrome;
  1085.         Firebug.setPlacement(newPlacement);
  1086.  
  1087.         // reattach all contexts to the new chrome
  1088.         TabWatcher.iterateContexts(function reattach(context)
  1089.         {
  1090.             context.reattach(oldChrome, newChrome);
  1091.  
  1092.             Firebug.reattachContext(context.browser, context);
  1093.         });
  1094.     },
  1095.  
  1096.     detachBar: function(context)
  1097.     {
  1098.         if (!context)
  1099.         {
  1100.             var browser = Firebug.chrome.getCurrentBrowser();
  1101.             var created = TabWatcher.watchBrowser(browser);  // create a context for this page
  1102.             if (!created)
  1103.             {
  1104.                 return null;
  1105.             }
  1106.             context = TabWatcher.getContextByWindow(browser.contentWindow);
  1107.         }
  1108.  
  1109.         if (Firebug.isDetached())  // can be set true attachBrowser
  1110.         {
  1111.             Firebug.chrome.focus();
  1112.             return null;
  1113.         }
  1114.  
  1115.         this.showBar(false);  // don't show in browser.xul now
  1116.  
  1117.         Firebug.chrome.setFirebugContext(context);  // make sure the FirebugContext agrees with context
  1118.         FirebugContext = context;
  1119.  
  1120.         this.setPlacement("detached");  // we'll reset it in the new window, but we seem to race with code in this window.
  1121.  
  1122.         var args = {
  1123.             FBL: FBL,
  1124.             Firebug: this,
  1125.             browser: context.browser,
  1126.             FirebugContext: window.FirebugContext
  1127.         };
  1128.         var win = openWindow("Firebug", "chrome://firebug/content/firebug.xul", "", args);
  1129.  
  1130.         return win;
  1131.     },
  1132.  
  1133.     syncBar: function()  // show firebug if we should
  1134.     {
  1135.         var browser = FirebugChrome.getCurrentBrowser();
  1136.         this.showBar(browser && browser.showFirebug);  // implicitly this is operating in the chrome of browser.xul
  1137.     },
  1138.  
  1139.     onClickStatusIcon: function(context, event)
  1140.     {
  1141.         if (event.button != 0)
  1142.             return;
  1143.         else if (isControl(event))
  1144.             this.toggleDetachBar(true);
  1145.         else if (context && context.errorCount)
  1146.             Firebug.toggleBar(undefined, 'console');
  1147.         else
  1148.             this.toggleBar();
  1149.     },
  1150.  
  1151.     onClickStatusText: function(context, event)
  1152.     {
  1153.         if (event.button != 0)
  1154.             return;
  1155.  
  1156.         if (!context || !context.errorCount)
  1157.             return;
  1158.  
  1159.         var panel = Firebug.chrome.getSelectedPanel();
  1160.         if (panel && panel.name != "console")
  1161.         {
  1162.             Firebug.chrome.selectPanel("console");
  1163.             cancelEvent(event);
  1164.         }
  1165.     },
  1166.  
  1167.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1168.  
  1169.     resetAllOptions: function(confirm)  // to default state
  1170.     {
  1171.         if (confirm)
  1172.         {
  1173.             if (!promptService.confirm(null, $STR("Firebug"), $STR("confirmation.Reset_All_Firebug_Options")))
  1174.                 return;
  1175.         }
  1176.  
  1177.         var preferences = prefs.getChildList("extensions.firebug", {});
  1178.         for (var i = 0; i < preferences.length; i++)
  1179.         {
  1180.             if (preferences[i].indexOf("DBG_") == -1 && preferences[i].indexOf("filterSystemURLs") == -1)
  1181.             {
  1182.                 if (prefs.prefHasUserValue(preferences[i]))  // avoid exception
  1183.                     prefs.clearUserPref(preferences[i]);
  1184.             }
  1185.             else
  1186.             {
  1187.             }
  1188.         }
  1189.  
  1190.         TabWatcher.iterateContexts( function clearBPs(context)
  1191.         {
  1192.             Firebug.Debugger.clearAllBreakpoints(context);
  1193.         });
  1194.     },
  1195.  
  1196.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1197.     // Panels
  1198.  
  1199.     getPanelType: function(panelName)
  1200.     {
  1201.         if (panelTypeMap.hasOwnProperty(panelName))
  1202.             return panelTypeMap[panelName];
  1203.         else
  1204.             return null;
  1205.     },
  1206.  
  1207.     getPanelTitle: function(panelType)
  1208.     {
  1209.         return panelType.prototype.title ? panelType.prototype.title
  1210.             : FBL.$STR("Panel-"+panelType.prototype.name);
  1211.     },
  1212.  
  1213.     getMainPanelTypes: function(context)
  1214.     {
  1215.         var resultTypes = [];
  1216.  
  1217.         for (var i = 0; i < panelTypes.length; ++i)
  1218.         {
  1219.             var panelType = panelTypes[i];
  1220.             if (!panelType.prototype.parentPanel)
  1221.                 resultTypes.push(panelType);
  1222.         }
  1223.  
  1224.         if (context.panelTypes)
  1225.         {
  1226.             for (var i = 0; i < context.panelTypes.length; ++i)
  1227.             {
  1228.                 var panelType = context.panelTypes[i];
  1229.                 if (!panelType.prototype.parentPanel)
  1230.                     resultTypes.push(panelType);
  1231.             }
  1232.         }
  1233.  
  1234.         return resultTypes;
  1235.     },
  1236.  
  1237.     getSidePanelTypes: function(context, mainPanel)
  1238.     {
  1239.         if (!mainPanel)
  1240.             return [];
  1241.  
  1242.         var resultTypes = [];
  1243.  
  1244.         for (var i = 0; i < panelTypes.length; ++i)
  1245.         {
  1246.             var panelType = panelTypes[i];
  1247.             if (panelType.prototype.parentPanel && (panelType.prototype.parentPanel == mainPanel.name) )
  1248.                 resultTypes.push(panelType);
  1249.         }
  1250.  
  1251.         if (context.panelTypes)
  1252.         {
  1253.             for (var i = 0; i < context.panelTypes.length; ++i)
  1254.             {
  1255.                 var panelType = context.panelTypes[i];
  1256.                 if (panelType.prototype.parentPanel == mainPanel.name)
  1257.                     resultTypes.push(panelType);
  1258.             }
  1259.         }
  1260.  
  1261.         resultTypes.sort(function(a, b)
  1262.         {
  1263.             return a.prototype.order < b.prototype.order ? -1 : 1;
  1264.         });
  1265.  
  1266.         return resultTypes;
  1267.     },
  1268.  
  1269.     /**
  1270.      * Gets an object containing the state of the panel from the last time
  1271.      * it was displayed before one or more page reloads.
  1272.      * The 'null' return here is a too-subtle signal to the panel code in bindings.xml.
  1273.      * Note that panel.context may not have a persistedState, but in addition the persisted state for panel.name may be null.
  1274.      */
  1275.     getPanelState: function(panel)
  1276.     {
  1277.         var persistedState = panel.context.persistedState;
  1278.         if (!persistedState || !persistedState.panelState)
  1279.             return null;
  1280.  
  1281.         return persistedState.panelState[panel.name];
  1282.     },
  1283.  
  1284.     showPanel: function(browser, panel)
  1285.     {
  1286.         dispatch(modules, "showPanel", [browser, panel]);
  1287.     },
  1288.  
  1289.     showSidePanel: function(browser, sidePanel)
  1290.     {
  1291.         dispatch(modules, "showSidePanel", [browser, sidePanel]);
  1292.     },
  1293.  
  1294.     reattachContext: function(browser, context)
  1295.     {
  1296.         dispatch(modules, "reattachContext", [browser, context]);
  1297.     },
  1298.  
  1299.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1300.     // URL mapping
  1301.  
  1302.     getObjectByURL: function(context, url)
  1303.     {
  1304.         for (var i = 0; i < modules.length; ++i)
  1305.         {
  1306.             var object = modules[i].getObjectByURL(context, url);
  1307.             if (object)
  1308.                 return object;
  1309.         }
  1310.     },
  1311.  
  1312.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1313.     // Reps
  1314.  
  1315.     getRep: function(object)
  1316.     {
  1317.         var type = typeof(object);
  1318.         if (type == 'object' && object instanceof String)
  1319.             type = 'string';
  1320.  
  1321.         for (var i = 0; i < reps.length; ++i)
  1322.         {
  1323.             var rep = reps[i];
  1324.             try
  1325.             {
  1326.                 if (rep.supportsObject(object, type))
  1327.                 {
  1328.                     return rep;
  1329.                 }
  1330.             }
  1331.             catch (exc)
  1332.             {
  1333.             }
  1334.         }
  1335.  
  1336.         return (type == 'function')?defaultFuncRep:defaultRep;
  1337.     },
  1338.  
  1339.     getRepObject: function(node)
  1340.     {
  1341.         var target = null;
  1342.         for (var child = node; child; child = child.parentNode)
  1343.         {
  1344.             if (hasClass(child, "repTarget"))
  1345.                 target = child;
  1346.  
  1347.             if (child.repObject)
  1348.             {
  1349.                 if (!target && hasClass(child, "repIgnore"))
  1350.                     break;
  1351.                 else
  1352.                     return child.repObject;
  1353.             }
  1354.         }
  1355.     },
  1356.  
  1357.     getRepNode: function(node)
  1358.     {
  1359.         for (var child = node; child; child = child.parentNode)
  1360.         {
  1361.             if (child.repObject)
  1362.                 return child;
  1363.         }
  1364.     },
  1365.  
  1366.     getElementByRepObject: function(element, object)
  1367.     {
  1368.         for (var child = element.firstChild; child; child = child.nextSibling)
  1369.         {
  1370.             if (child.repObject == object)
  1371.                 return child;
  1372.         }
  1373.     },
  1374.  
  1375.     /**
  1376.      * Takes an element from a panel document and finds the owning panel.
  1377.      */
  1378.     getElementPanel: function(element)
  1379.     {
  1380.         for (; element; element = element.parentNode)
  1381.         {
  1382.             if (element.ownerPanel)
  1383.                 return element.ownerPanel;
  1384.         }
  1385.     },
  1386.  
  1387.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1388.  
  1389.     visitWebsite: function(which)
  1390.     {
  1391.         openNewTab(firebugURLs[which]);
  1392.     },
  1393.  
  1394.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1395.     // nsISupports
  1396.  
  1397.     QueryInterface : function(iid)
  1398.     {
  1399.         if (iid.equals(nsISupports))
  1400.         {
  1401.             return this;
  1402.         }
  1403.  
  1404.         throw Components.results.NS_NOINTERFACE;
  1405.     },
  1406.  
  1407.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1408.     // nsIPrefObserver
  1409.  
  1410.     observe: function(subject, topic, data)
  1411.     {
  1412.         if (data.indexOf("extensions.") == -1)
  1413.             return;
  1414.  
  1415.         if (data.substring(0, Firebug.prefDomain.length) == Firebug.prefDomain)
  1416.             var domain = Firebug.prefDomain;
  1417.         if (data.substring(0, Firebug.servicePrefDomain.length) == Firebug.servicePrefDomain)
  1418.             var domain = Firebug.servicePrefDomain;
  1419.  
  1420.         if (domain)
  1421.         {
  1422.             var name = data.substr(domain.length+1);
  1423.             var value = this.getPref(domain, name);
  1424.             this.updatePref(name, value);
  1425.         }
  1426.  
  1427.         if (topic == "nsPref:changed")
  1428.         {
  1429.             if (data.indexOf(".enableSites") != -1)
  1430.             {
  1431.                 dispatch(modules, "onEnablePrefChange", [data]);
  1432.             }
  1433.         }
  1434.     },
  1435.  
  1436.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1437.     // nsIFireBugClient  These are per Firefox XUL window callbacks
  1438.  
  1439.     enableXULWindow: function()  // Called when the first context is created.
  1440.     {
  1441.         if (window.closed)
  1442.             throw new Error("enableXULWindow window is closed!!");
  1443.  
  1444.         dispatch(modules, "enable", [FirebugChrome]);  // allows errors to flow thru fbs and callbacks to supportWindow to begin
  1445.     },
  1446.  
  1447.     disableXULWindow: function()
  1448.     {
  1449.         dispatch(modules, "disable", [FirebugChrome]);
  1450.     },
  1451.  
  1452.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1453.     // These are XUL window level call backs and should be moved into Firebug where is says nsIFirebugClient
  1454.     // xxxHonza: so I did
  1455.  
  1456.     onPauseJSDRequested: function(rejection)
  1457.     {
  1458.         if (top.FirebugContext)  // then we are active in this browser.xul
  1459.             rejection.push(true); // so reject the request
  1460.  
  1461.         dispatch2(Firebug.Debugger.fbListeners, "onPauseJSDRequested", [rejection]);
  1462.     },
  1463.  
  1464.     onJSDActivate: function(active, why)  // just before hooks are set
  1465.     {
  1466.         this.setIsJSDActive(active);
  1467.  
  1468.         dispatch2(Firebug.Debugger.fbListeners, "onJSDActivate", [active, why]);
  1469.     },
  1470.  
  1471.     onJSDDeactivate: function(active, why)
  1472.     {
  1473.         this.setIsJSDActive(active);
  1474.  
  1475.         dispatch2(Firebug.Debugger.fbListeners, "onJSDDeactivate", [active, why]);
  1476.     },
  1477.  
  1478.     setIsJSDActive: function(active)  // should only be call on the jsd activation events, so it correctly reflects jsd state
  1479.     {
  1480.         if (active)
  1481.             $('fbStatusIcon').setAttribute("script", "on");
  1482.         else
  1483.             $('fbStatusIcon').setAttribute("script", "off");
  1484.  
  1485.     },
  1486.  
  1487.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1488.     // Placement
  1489.  
  1490.     isDetached: function()
  1491.     {
  1492.         return Firebug.placement == PLACEMENT_DETACHED;
  1493.     },
  1494.  
  1495.     isMinimized: function()
  1496.     {
  1497.         return Firebug.placement == PLACEMENT_MINIMIZED;
  1498.     },
  1499.  
  1500.     isInBrowser: function()
  1501.     {
  1502.         return Firebug.placement == PLACEMENT_INBROWSER;
  1503.     },
  1504.  
  1505.     placements: ["none", "inBrowser", "detached", "minimized"],
  1506.  
  1507.     placement: 1,
  1508.  
  1509.     setPlacement: function(toPlacement)
  1510.     {
  1511.         // TODO : This should probably be an event so others can link into this
  1512.         Firebug.chrome.$("fbSearchBox").hideOptions();
  1513.  
  1514.         for (var i = 0; i < Firebug.placements.length; i++)
  1515.         {
  1516.             if (toPlacement == Firebug.placements[i])
  1517.             {
  1518.                 if (Firebug.placement != i) // then we are changing the value
  1519.                 {
  1520.                     Firebug.placement = i;
  1521.                     delete Firebug.previousPlacement;
  1522.                     Firebug.setPref(Firebug.prefDomain, "previousPlacement", Firebug.placement);
  1523.                     Firebug.resetTooltip();
  1524.                 }
  1525.                 return Firebug.placement;
  1526.             }
  1527.         }
  1528.         throw new Error("Firebug.setPlacement cannot match "+toPlacement+" as a placement");
  1529.     },
  1530.  
  1531.     getPlacement: function()
  1532.     {
  1533.         return Firebug.placements[Firebug.placement];
  1534.     },
  1535.  
  1536.     openMinimized: function()
  1537.     {
  1538.         if (!Firebug.previousPlacement)
  1539.             Firebug.previousPlacement = Firebug.getPref(Firebug.prefDomain, "previousPlacement");
  1540.  
  1541.         return (Firebug.previousPlacement && (Firebug.previousPlacement == PLACEMENT_MINIMIZED) )
  1542.     },
  1543.  
  1544.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1545.     // TabWatcher Listener
  1546.  
  1547.     getContextType: function()
  1548.     {
  1549.         return Firebug.TabContext;
  1550.     },
  1551.  
  1552.     shouldShowContext: function(context)
  1553.     {
  1554.         return dispatch2(modules, "shouldShowContext", [context]);
  1555.     },
  1556.  
  1557.     shouldCreateContext: function(browser, url, userCommands)
  1558.     {
  1559.         return dispatch2(modules, "shouldCreateContext", [browser, url, userCommands]);
  1560.     },
  1561.  
  1562.     shouldNotCreateContext: function(browser, url, userCommands)
  1563.     {
  1564.         return dispatch2(modules, "shouldNotCreateContext", [browser, url, userCommands]);
  1565.     },
  1566.  
  1567.     initContext: function(context, persistedState)  // called after a context is created.
  1568.     {
  1569.         context.panelName = context.browser.panelName;
  1570.         if (context.browser.sidePanelNames)
  1571.             context.sidePanelNames = context.browser.sidePanelNames;
  1572.  
  1573.  
  1574.         dispatch(modules, "initContext", [context, persistedState]);
  1575.  
  1576.         this.updateActiveContexts(context); // a newly created context is active
  1577.  
  1578.         Firebug.chrome.setFirebugContext(context); // a newly created context becomes the default for the view
  1579.         FirebugContext = context;
  1580.  
  1581.         if (deadWindowTimeout)
  1582.             this.rescueWindow(context.browser); // if there is already a window, clear showDetached.
  1583.     },
  1584.  
  1585.     updateActiveContexts: function(context) // this should be the only method to call suspend and resume.
  1586.     {
  1587.         if (context)  // either a new context or revisiting an old one
  1588.         {
  1589.             if(!this.hadFirstContext)  // then we need to enable the panels iff the prefs say so
  1590.             {
  1591.                 this.hadFirstContext = true;
  1592.                 Firebug.ModuleManager.obeyPrefs(context);
  1593.             }
  1594.             if (Firebug.getSuspended())
  1595.                 Firebug.resume();  // This will cause onResumeFirebug for every context including this one.
  1596.         }
  1597.         else // this browser has no context
  1598.         {
  1599.             Firebug.suspend();
  1600.         }
  1601.  
  1602.         Firebug.resetTooltip();
  1603.     },
  1604.  
  1605.     showContext: function(browser, context)  // TabWatcher showContext. null context means we don't debug that browser
  1606.     {
  1607.         if (clearContextTimeout)
  1608.         {
  1609.             clearTimeout(clearContextTimeout);
  1610.             clearContextTimeout = 0;
  1611.         }
  1612.  
  1613.         FirebugContext = context;
  1614.         Firebug.chrome.setFirebugContext(context); // the context becomes the default for its view
  1615.         this.updateActiveContexts(context);  // resume, after setting FirebugContext
  1616.  
  1617.         dispatch(modules, "showContext", [browser, context]);  // tell modules we may show UI
  1618.  
  1619.         // user wants detached but we are not yet
  1620.         if (Firebug.openInWindow && !Firebug.isDetached())
  1621.         {
  1622.             if (context && !Firebug.isMinimized()) // don't detach if it's minimized 2067
  1623.                 this.detachBar(context);  //   the placement will be set once the external window opens
  1624.             else  // just make sure we are not showing
  1625.                 this.showBar(false);
  1626.  
  1627.             return;
  1628.         }
  1629.  
  1630.         // previous browser.xul had placement minimized
  1631.         if (Firebug.openMinimized() && !Firebug.isMinimized())
  1632.         {
  1633.             this.minimizeBar();
  1634.             return;
  1635.         }
  1636.  
  1637.         if (Firebug.isMinimized())
  1638.             this.showBar(false);  // don't show, we are minimized
  1639.         else if (Firebug.isDetached())
  1640.             this.syncResumeBox(context);
  1641.         else  // inBrowser
  1642.             this.showBar(context?true:false);
  1643.  
  1644.     },
  1645.  
  1646.     syncResumeBox: function(context)
  1647.     {
  1648.         var contentBox = Firebug.chrome.$('fbContentBox');
  1649.         var resumeBox = Firebug.chrome.$('fbResumeBox');
  1650.  
  1651.         if (!resumeBox) // the showContext is being called before the reattachContext, we'll get a second showContext
  1652.             return;
  1653.  
  1654.         if (context)
  1655.         {
  1656.             collapse(contentBox, false);
  1657.             Firebug.chrome.syncPanel();
  1658.             collapse(resumeBox, true);
  1659.         }
  1660.         else
  1661.         {
  1662.             collapse(contentBox, true);
  1663.             collapse(resumeBox, false);
  1664.             Firebug.chrome.window.document.title = $STR("Firebug - inactive for selected Firefox tab");
  1665.         }
  1666.     },
  1667.  
  1668.     unwatchBrowser: function(browser)  // the context for this browser has been destroyed and removed
  1669.     {
  1670.         Firebug.updateActiveContexts(null);
  1671.     },
  1672.  
  1673.     // Either a top level or a frame, (interior window) for an exist context is seen by the tabWatcher.
  1674.     watchWindow: function(context, win)
  1675.     {
  1676.         for (var panelName in context.panelMap)
  1677.         {
  1678.             var panel = context.panelMap[panelName];
  1679.             panel.watchWindow(win);
  1680.         }
  1681.  
  1682.         dispatch(modules, "watchWindow", [context, win]);
  1683.     },
  1684.  
  1685.     unwatchWindow: function(context, win)
  1686.     {
  1687.         for (var panelName in context.panelMap)
  1688.         {
  1689.             var panel = context.panelMap[panelName];
  1690.             panel.unwatchWindow(win);
  1691.         }
  1692.         dispatch(modules, "unwatchWindow", [context, win]);
  1693.     },
  1694.  
  1695.     loadedContext: function(context)
  1696.     {
  1697.         if (!context.browser.currentURI)
  1698.             FBTrace.sysout("firebug.loadedContext problem browser ", context.browser);
  1699.  
  1700.         dispatch(modules, "loadedContext", [context]);
  1701.     },
  1702.  
  1703.     destroyContext: function(context, persistedState, browser)
  1704.     {
  1705.         if (!context)  // then we are called just to clean up
  1706.             return;
  1707.  
  1708.         dispatch(modules, "destroyContext", [context, persistedState]);
  1709.  
  1710.         if (FirebugContext == context)
  1711.             Firebug.chrome.setFirebugContext(null);  // FirebugContext is about to be destroyed
  1712.  
  1713.         var browser = context.browser;
  1714.         // Persist remnants of the context for restoration if the user reloads
  1715.         browser.panelName = context.panelName;
  1716.         browser.sidePanelNames = context.sidePanelNames;
  1717.  
  1718.         // next the context is deleted and removed from the TabWatcher, we clean up in unWatchBrowser
  1719.     },
  1720.  
  1721.     onSourceFileCreated: function(context, sourceFile)
  1722.     {
  1723.         dispatch(modules, "onSourceFileCreated", [context, sourceFile]);
  1724.     },
  1725.  
  1726.     //*********************************************************************************************
  1727.  
  1728.     getTabForWindow: function(aWindow)
  1729.     {
  1730.         aWindow = getRootWindow(aWindow);
  1731.  
  1732.         if (!aWindow || !this.tabBrowser.getBrowserIndexForDocument)
  1733.             return null;
  1734.  
  1735.         try {
  1736.             var targetDoc = aWindow.document;
  1737.  
  1738.             var tab = null;
  1739.             var targetBrowserIndex = this.tabBrowser.getBrowserIndexForDocument(targetDoc);
  1740.  
  1741.             if (targetBrowserIndex != -1)
  1742.             {
  1743.                 tab = this.tabBrowser.tabContainer.childNodes[targetBrowserIndex];
  1744.                 return tab;
  1745.             }
  1746.         } catch (ex) {}
  1747.  
  1748.         return null;
  1749.     },
  1750.  
  1751.     getTabIdForWindow: function(win)
  1752.     {
  1753.         var tab = this.getTabForWindow(win);
  1754.         return tab ? tab.linkedPanel : null;
  1755.     },
  1756. };
  1757.  
  1758. // ************************************************************************************************
  1759.  
  1760. /**
  1761.  * Support for listeners registration. This object also extended by Firebug.Module so,
  1762.  * all modules supports listening automatically. Notice that array of listeners
  1763.  * is created for each intance of a module within initialize method. Thus all derived
  1764.  * module classes must ensure that Firebug.Module.initialize method is called for the
  1765.  * super class.
  1766.  */
  1767. Firebug.Listener = function()
  1768. {
  1769.     // The array is created when the first listeners is added.
  1770.     // It can't be created here since derived objects would share
  1771.     // the same array.
  1772.     this.fbListeners = null;
  1773. }
  1774. Firebug.Listener.prototype =
  1775. {
  1776.     addListener: function(listener)
  1777.     {
  1778.         if (!this.fbListeners)
  1779.             this.fbListeners = []; // delay the creation until the objects are created so 'this' causes new array for each module
  1780.  
  1781.         this.fbListeners.push(listener);
  1782.     },
  1783.  
  1784.     removeListener: function(listener)
  1785.     {
  1786.         remove(this.fbListeners, listener);  // if this.fbListeners is null, remove is being called with no add
  1787.     }
  1788. };
  1789.  
  1790. // ************************************************************************************************
  1791.  
  1792. /**
  1793.  * @module Base class for all modules. Every derived module object must be registered using
  1794.  * <code>Firebug.registerModule</code> method. There is always one instance of a module object
  1795.  * per browser window.
  1796.  */
  1797. Firebug.Module = extend(new Firebug.Listener(),
  1798. /** @lends Firebug.Module */
  1799. {
  1800.     /**
  1801.      * Called by Firebug when Firefox window is opened.
  1802.      */
  1803.     initialize: function()
  1804.     {
  1805.  
  1806.     },
  1807.  
  1808.     /**
  1809.      * Called when the UI is ready for context creation.
  1810.      * Used by chromebug; normally FrameProgressListener events trigger UI synchronization,
  1811.      * this event allows sync without progress events.
  1812.      */
  1813.     initializeUI: function(detachArgs)
  1814.     {
  1815.     },
  1816.  
  1817.     /**
  1818.      * Called by Firebug when Firefox window is closed.
  1819.      */
  1820.     shutdown: function()
  1821.     {
  1822.  
  1823.     },
  1824.  
  1825.     /**
  1826.      * Called when a new context is created but before the page is loaded.
  1827.      */
  1828.     initContext: function(context, persistedState)
  1829.     {
  1830.     },
  1831.  
  1832.     /**
  1833.      * Called after a context is detached to a separate window;
  1834.      */
  1835.     reattachContext: function(browser, context)
  1836.     {
  1837.     },
  1838.  
  1839.     /**
  1840.      * Called when a context is destroyed. Module may store info on persistedState for reloaded pages.
  1841.      */
  1842.     destroyContext: function(context, persistedState)
  1843.     {
  1844.     },
  1845.  
  1846.     /**
  1847.      * Called when attaching to a window (top-level or frame).
  1848.      */
  1849.     watchWindow: function(context, win)
  1850.     {
  1851.     },
  1852.  
  1853.     /**
  1854.      * Called when unwatching a window (top-level or frame).
  1855.      */
  1856.     unwatchWindow: function(context, win)
  1857.     {
  1858.     },
  1859.  
  1860.     // Called when a FF tab is create or activated (user changes FF tab)
  1861.     // Called after context is created or with context == null (to abort?)
  1862.     showContext: function(browser, context)
  1863.     {
  1864.     },
  1865.  
  1866.     /**
  1867.      * Called after a context's page gets DOMContentLoaded
  1868.      */
  1869.     loadedContext: function(context)
  1870.     {
  1871.     },
  1872.  
  1873.     /*
  1874.      * After "onSelectingPanel", a panel has been selected but is not yet visible
  1875.      */
  1876.     showPanel: function(browser, panel)
  1877.     {
  1878.     },
  1879.  
  1880.     showSidePanel: function(browser, sidePanel)
  1881.     {
  1882.     },
  1883.  
  1884.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1885.  
  1886.     updateOption: function(name, value)
  1887.     {
  1888.     },
  1889.  
  1890.     getObjectByURL: function(context, url)
  1891.     {
  1892.     },
  1893.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1894.     // intermodule dependency
  1895.  
  1896.     // caller needs module. win maybe context.window or iframe in context.window.
  1897.     // true means module is ready now, else getting ready
  1898.     isReadyElsePreparing: function(context, win)
  1899.     {
  1900.     },
  1901. });
  1902.  
  1903. //************************************************************************************************
  1904.  
  1905. Firebug.Extension =
  1906. {
  1907.     acceptContext: function(win,uri)
  1908.     {
  1909.         return false;
  1910.     },
  1911.  
  1912.     declineContext: function(win,uri)
  1913.     {
  1914.         return false;
  1915.     }
  1916. };
  1917.  
  1918. // ************************************************************************************************
  1919.  
  1920. /**
  1921.  * @panel Base class for all panels. Every derived panel must define a constructor and
  1922.  * register with <code>Firebug.registerPanel</code> method. An instance of the panel
  1923.  * object is created by the framework for each browser tab where Firebug is activated.
  1924.  */
  1925. Firebug.Panel =
  1926. /** @lends Firebug.Panel */
  1927. {
  1928.     searchable: false,
  1929.     editable: true,
  1930.     breakable: false,
  1931.     order: 2147483647,
  1932.     statusSeparator: "<",
  1933.  
  1934.     initialize: function(context, doc)
  1935.     {
  1936.         if (!context.browser)
  1937.         {
  1938.             return false;
  1939.         }
  1940.  
  1941.         this.context = context;
  1942.         this.document = doc;
  1943.  
  1944.         this.panelNode = doc.createElement("div");
  1945.         this.panelNode.ownerPanel = this;
  1946.  
  1947.         setClass(this.panelNode, "panelNode panelNode-"+this.name+" contextUID="+context.uid);
  1948.  
  1949.         // Load persistent content if any.
  1950.         var persistedState = Firebug.getPanelState(this);
  1951.         if (persistedState)
  1952.         {
  1953.             this.persistContent = persistedState.persistContent;
  1954.             if (this.persistContent && persistedState.panelNode)
  1955.                 this.loadPersistedContent(persistedState);
  1956.         }
  1957.  
  1958.         doc.body.appendChild(this.panelNode);
  1959.  
  1960.         // Update panel's tab in case the break-on-next (BON) is active.
  1961.         var shouldBreak = this.shouldBreakOnNext();
  1962.         Firebug.Breakpoint.updatePanelTab(this, shouldBreak);
  1963.  
  1964.         this.initializeNode(this.panelNode);
  1965.     },
  1966.  
  1967.     destroy: function(state) // Panel may store info on state
  1968.     {
  1969.         if (this.panelNode)
  1970.         {
  1971.             if (this.persistContent)
  1972.                 this.savePersistedContent(state);
  1973.             else
  1974.                 delete state.persistContent;
  1975.  
  1976.             delete this.panelNode.ownerPanel;
  1977.         }
  1978.  
  1979.         this.destroyNode();
  1980.  
  1981.         clearDomplate(this.panelNode);
  1982.     },
  1983.  
  1984.     savePersistedContent: function(state)
  1985.     {
  1986.         state.panelNode = this.panelNode;
  1987.         state.persistContent = this.persistContent;
  1988.     },
  1989.  
  1990.     loadPersistedContent: function(persistedState)
  1991.     {
  1992.         // move the nodes from the persistedState to the panel
  1993.         while (persistedState.panelNode.firstChild)
  1994.             this.panelNode.appendChild(persistedState.panelNode.firstChild);
  1995.  
  1996.         scrollToBottom(this.panelNode);
  1997.     },
  1998.  
  1999.     // called when a panel in one XUL window is about to appear in another one.
  2000.     detach: function(oldChrome, newChrome)
  2001.     {
  2002.     },
  2003.  
  2004.     reattach: function(doc)  // this is how a panel in one window reappears in another window; lazy called
  2005.     {
  2006.         this.document = doc;
  2007.  
  2008.         if (this.panelNode)
  2009.         {
  2010.             this.panelNode = doc.adoptNode(this.panelNode, true);
  2011.             this.panelNode.ownerPanel = this;
  2012.             doc.body.appendChild(this.panelNode);
  2013.         }
  2014.     },
  2015.  
  2016.     // Called at the end of module.initialize; addEventListener-s here
  2017.     initializeNode: function(myPanelNode)
  2018.     {
  2019.     },
  2020.  
  2021.     // removeEventListener-s here.
  2022.     destroyNode: function()
  2023.     {
  2024.     },
  2025.  
  2026.     show: function(state)  // persistedPanelState plus non-persisted hide() values
  2027.     {
  2028.     },
  2029.  
  2030.     hide: function(state)  // store info on state for next show.
  2031.     {
  2032.     },
  2033.  
  2034.     watchWindow: function(win)
  2035.     {
  2036.     },
  2037.  
  2038.     unwatchWindow: function(win)
  2039.     {
  2040.     },
  2041.  
  2042.     updateOption: function(name, value)
  2043.     {
  2044.     },
  2045.  
  2046.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2047.  
  2048.     /**
  2049.      * Toolbar helpers
  2050.      */
  2051.     showToolbarButtons: function(buttonsId, show)
  2052.     {
  2053.         try
  2054.         {
  2055.             var buttons = Firebug.chrome.$(buttonsId);
  2056.             collapse(buttons, !show);
  2057.         }
  2058.         catch (exc)
  2059.         {
  2060.         }
  2061.     },
  2062.  
  2063.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2064.  
  2065.     /**
  2066.      * Returns a number indicating the view's ability to inspect the object.
  2067.      *
  2068.      * Zero means not supported, and higher numbers indicate specificity.
  2069.      */
  2070.     supportsObject: function(object)
  2071.     {
  2072.         return 0;
  2073.     },
  2074.  
  2075.     hasObject: function(object)  // beyond type testing, is this object selectable?
  2076.     {
  2077.         return false;
  2078.     },
  2079.  
  2080.     navigate: function(object)
  2081.     {
  2082.         if (!object)
  2083.             object = this.getDefaultLocation(this.context);
  2084.         if (!object)
  2085.             object = null;  // not undefined.
  2086.  
  2087.         if ( !this.location || (object != this.location) )  // if this.location undefined, may set to null
  2088.         {
  2089.             this.location = object;
  2090.             this.updateLocation(object);
  2091.  
  2092.             dispatch(Firebug.uiListeners, "onPanelNavigate", [object, this]);
  2093.         }
  2094.     },
  2095.  
  2096.     updateLocation: function(object)  // if the module can return null from getDefaultLocation, then it must handle it here.
  2097.     {
  2098.     },
  2099.  
  2100.     /**
  2101.      * Navigates to the next document whose match parameter returns true.
  2102.      */
  2103.     navigateToNextDocument: function(match, reverse)
  2104.     {
  2105.         // This is an approximation of the UI that is displayed by the location
  2106.         // selector. This should be close enough, although it may be better
  2107.         // to simply generate the sorted list within the module, rather than
  2108.         // sorting within the UI.
  2109.         var self = this;
  2110.         function compare(a, b) {
  2111.             var locA = self.getObjectDescription(a);
  2112.             var locB = self.getObjectDescription(b);
  2113.             if(locA.path > locB.path)
  2114.                 return 1;
  2115.             if(locA.path < locB.path)
  2116.                 return -1;
  2117.             if(locA.name > locB.name)
  2118.                 return 1;
  2119.             if(locA.name < locB.name)
  2120.                 return -1;
  2121.             return 0;
  2122.         }
  2123.         var allLocs = this.getLocationList().sort(compare);
  2124.         for (var curPos = 0; curPos < allLocs.length && allLocs[curPos] != this.location; curPos++);
  2125.  
  2126.         function transformIndex(index) {
  2127.             if (reverse) {
  2128.                 // For the reverse case we need to implement wrap around.
  2129.                 var intermediate = curPos - index - 1;
  2130.                 return (intermediate < 0 ? allLocs.length : 0) + intermediate;
  2131.             } else {
  2132.                 return (curPos + index + 1) % allLocs.length;
  2133.             }
  2134.         };
  2135.  
  2136.         for (var next = 0; next < allLocs.length - 1; next++)
  2137.         {
  2138.             var object = allLocs[transformIndex(next)];
  2139.  
  2140.             if (match(object))
  2141.             {
  2142.                 this.navigate(object);
  2143.                 return object;
  2144.             }
  2145.         }
  2146.     },
  2147.  
  2148.     select: function(object, forceUpdate)
  2149.     {
  2150.         if (!object)
  2151.             object = this.getDefaultSelection(this.context);
  2152.  
  2153.         if (forceUpdate || object != this.selection)
  2154.         {
  2155.             this.selection = object;
  2156.             this.updateSelection(object);
  2157.  
  2158.             dispatch(Firebug.uiListeners, "onObjectSelected", [object, this]);
  2159.         }
  2160.     },
  2161.  
  2162.  
  2163.     updateSelection: function(object)
  2164.     {
  2165.     },
  2166.  
  2167.     refresh: function()
  2168.     {
  2169.  
  2170.     },
  2171.  
  2172.     markChange: function(skipSelf)
  2173.     {
  2174.         if (this.dependents)
  2175.         {
  2176.             if (skipSelf)
  2177.             {
  2178.                 for (var i = 0; i < this.dependents.length; ++i)
  2179.                 {
  2180.                     var panelName = this.dependents[i];
  2181.                     if (panelName != this.name)
  2182.                         this.context.invalidatePanels(panelName);
  2183.                 }
  2184.             }
  2185.             else
  2186.                 this.context.invalidatePanels.apply(this.context, this.dependents);
  2187.         }
  2188.     },
  2189.  
  2190.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2191.  
  2192.     startInspecting: function()
  2193.     {
  2194.     },
  2195.  
  2196.     stopInspecting: function(object, cancelled)
  2197.     {
  2198.     },
  2199.  
  2200.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2201.  
  2202.     search: function(text, reverse)
  2203.     {
  2204.     },
  2205.  
  2206.     /**
  2207.      * Retrieves the search options that this modules supports.
  2208.      * This is used by the search UI to present the proper options.
  2209.      */
  2210.     getSearchOptionsMenuItems: function()
  2211.     {
  2212.         return [
  2213.             Firebug.Search.searchOptionMenu("search.Case_Sensitive", "searchCaseSensitive")
  2214.         ];
  2215.     },
  2216.  
  2217.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2218.  
  2219.     // Called when "Options" clicked. Return array of
  2220.     // {label: 'name', nol10n: true,  type: "checkbox", checked: <value>, command:function to set <value>}
  2221.     getOptionsMenuItems: function()
  2222.     {
  2223.         return null;
  2224.     },
  2225.  
  2226.     /*
  2227.      * Called by chrome.onContextMenu to build the context menu when this panel has focus.
  2228.      * See also FirebugRep for a similar function also called by onContextMenu
  2229.      * Extensions may monkey patch and chain off this call
  2230.      * @param object: the 'realObject', a model value, eg a DOM property
  2231.      * @param target: the HTML element clicked on.
  2232.      * @return an array of menu items.
  2233.      */
  2234.     getContextMenuItems: function(object, target)
  2235.     {
  2236.         return [];
  2237.     },
  2238.  
  2239.     getBreakOnMenuItems: function()
  2240.     {
  2241.         return [];
  2242.     },
  2243.  
  2244.     getEditor: function(target, value)
  2245.     {
  2246.     },
  2247.  
  2248.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2249.  
  2250.     getDefaultSelection: function(context)
  2251.     {
  2252.         return null;
  2253.     },
  2254.  
  2255.     browseObject: function(object)
  2256.     {
  2257.     },
  2258.  
  2259.     getPopupObject: function(target)
  2260.     {
  2261.         return Firebug.getRepObject(target);
  2262.     },
  2263.  
  2264.     getTooltipObject: function(target)
  2265.     {
  2266.         return Firebug.getRepObject(target);
  2267.     },
  2268.  
  2269.     showInfoTip: function(infoTip, x, y)
  2270.     {
  2271.  
  2272.     },
  2273.  
  2274.     getObjectPath: function(object)
  2275.     {
  2276.         return null;
  2277.     },
  2278.  
  2279.     // An array of objects that can be passed to getObjectLocation.
  2280.     // The list of things a panel can show, eg sourceFiles.
  2281.     // Only shown if panel.location defined and supportsObject true
  2282.     getLocationList: function()
  2283.     {
  2284.         return null;
  2285.     },
  2286.  
  2287.     getDefaultLocation: function(context)
  2288.     {
  2289.         return null;
  2290.     },
  2291.  
  2292.     getObjectLocation: function(object)
  2293.     {
  2294.         return "";
  2295.     },
  2296.  
  2297.     // Text for the location list menu eg script panel source file list
  2298.     // return.path: group/category label, return.name: item label
  2299.     getObjectDescription: function(object)
  2300.     {
  2301.         var url = this.getObjectLocation(object);
  2302.         return FBL.splitURLBase(url);
  2303.     },
  2304.  
  2305.     /*
  2306.      *  UI signal that a tab needs attention, eg Script panel is currently stopped on a breakpoint
  2307.      *  @param: show boolean, true turns on.
  2308.      */
  2309.     highlight: function(show)
  2310.     {
  2311.         var tab = this.getTab();
  2312.         if (!tab)
  2313.             return;
  2314.  
  2315.         if (show)
  2316.             tab.setAttribute("highlight", "true");
  2317.         else
  2318.             tab.removeAttribute("highlight");
  2319.     },
  2320.  
  2321.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2322.     // Support for Break On Next
  2323.  
  2324.     /**
  2325.      * Called by the framework when the user clicks on the Break On Next button.
  2326.      * @param {Boolean} armed Set to true if the Break On Next feature is
  2327.      * to be armed for action and set to false if the Break On Next should be disarmed.
  2328.      * If 'armed' is true, then the next call to shouldBreakOnNext should be |true|.
  2329.      */
  2330.     breakOnNext: function(armed)
  2331.     {
  2332.     },
  2333.  
  2334.     /**
  2335.      * Called when a panel is selected/displayed. The method should return true
  2336.      * if the Break On Next feature is currently armed for this panel.
  2337.      */
  2338.     shouldBreakOnNext: function()
  2339.     {
  2340.         return false;
  2341.     },
  2342.  
  2343.     /**
  2344.      * Returns labels for Break On Next tooltip (one for enabled and one for disabled state).
  2345.      * @param {Boolean} enabled Set to true if the Break On Next feature is
  2346.      * currently activated for this panel.
  2347.      */
  2348.     getBreakOnNextTooltip: function(enabled)
  2349.     {
  2350.         return null;
  2351.     },
  2352. };
  2353.  
  2354. //************************************************************************************************
  2355.  
  2356. Firebug.ActivablePanel = extend(Firebug.Panel,
  2357. {
  2358.     enablePanel: function(module)
  2359.     {
  2360.         var persistedPanelState = getPersistedState(this.context, this.name);
  2361.         persistedPanelState.enabled = true;
  2362.  
  2363.         var tab = this.getTab();
  2364.         if (tab)
  2365.             tab.setAttribute('aria-label', tab.textContent);
  2366.  
  2367.         // The panel was just enabled so, hide the disable message. Notice that
  2368.         // displaying this page replaces content of the panel.
  2369.         module.disabledPanelPage.hide(this);
  2370.  
  2371.         // xxxHonza: now I think this is the correct place to call Panel.show
  2372.         // If the enabled panel is currently visible, show the content.
  2373.         // It's necessary to update the toolbar.
  2374.         if (this.context.panelName == this.name)
  2375.         {
  2376.             this.show(persistedPanelState);
  2377.         }
  2378.  
  2379.         Firebug.resetTooltip();
  2380.     },
  2381.  
  2382.     disablePanel: function(module)
  2383.     {
  2384.         var persistedPanelState = getPersistedState(this.context, this.name);
  2385.         persistedPanelState.enabled = false;
  2386.  
  2387.         var tab = this.getTab();
  2388.         if (tab)
  2389.             tab.setAttribute('aria-label', tab.getAttribute('label') + " ("+ $STR('aria.labels.inactive panel') +")");
  2390.  
  2391.         // The panel was disabled so, show the disabled page. This page also replaces the
  2392.         // old content so, the panel is fresh empty after it's enabled again.
  2393.         module.disabledPanelPage.show(this);
  2394.  
  2395.         // Make sure toolbar buttons are not visible for disabled panels.
  2396.         if (this.context.panelName == this.name)
  2397.         {
  2398.             this.hide(persistedPanelState);
  2399.         }
  2400.  
  2401.         Firebug.resetTooltip();
  2402.     },
  2403.  
  2404.     getTab: function()
  2405.     {
  2406.         var chrome = Firebug.chrome;
  2407.  
  2408.         var tab = chrome.$("fbPanelBar2").getTab(this.name);
  2409.         if (!tab)
  2410.             tab = chrome.$("fbPanelBar1").getTab(this.name);
  2411.         return tab;
  2412.     },
  2413. });
  2414.  
  2415. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2416. /*
  2417.  * MeasureBox
  2418.  * To get pixels size.width and size.height:
  2419.  * <ul><li>     this.startMeasuring(view); </li>
  2420.  *     <li>     var size = this.measureText(lineNoCharsSpacer); </li>
  2421.  *     <li>     this.stopMeasuring(); </li>
  2422.  * </ul>
  2423.  */
  2424. Firebug.MeasureBox =
  2425. {
  2426.     startMeasuring: function(target)
  2427.     {
  2428.         if (!this.measureBox)
  2429.         {
  2430.             this.measureBox = target.ownerDocument.createElement("span");
  2431.             this.measureBox.className = "measureBox";
  2432.         }
  2433.  
  2434.         copyTextStyles(target, this.measureBox);
  2435.         target.ownerDocument.body.appendChild(this.measureBox);
  2436.     },
  2437.  
  2438.     getMeasuringElement: function()
  2439.     {
  2440.         return this.measureBox;
  2441.     },
  2442.  
  2443.     measureText: function(value)
  2444.     {
  2445.         this.measureBox.innerHTML = value ? escapeForSourceLine(value) : "m";
  2446.         return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1};
  2447.     },
  2448.  
  2449.     measureInputText: function(value)
  2450.     {
  2451.         value = value ? escapeForTextNode(value) : "m";
  2452.         if (!Firebug.showTextNodesWithWhitespace)
  2453.             value = value.replace(/\t/g,'mmmmmm').replace(/\ /g,'m');
  2454.         this.measureBox.innerHTML = value;
  2455.         return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1};
  2456.     },
  2457.  
  2458.     getBox: function(target)
  2459.     {
  2460.         var style = this.measureBox.ownerDocument.defaultView.getComputedStyle(this.measureBox, "");
  2461.         var box = getBoxFromStyles(style, this.measureBox);
  2462.         return box;
  2463.     },
  2464.  
  2465.     stopMeasuring: function()
  2466.     {
  2467.         this.measureBox.parentNode.removeChild(this.measureBox);
  2468.     }
  2469. };
  2470.  
  2471. // ************************************************************************************************
  2472.  
  2473. Firebug.Rep = domplate(
  2474. {
  2475.     className: "",
  2476.     inspectable: true,
  2477.  
  2478.     supportsObject: function(object, type)
  2479.     {
  2480.         return false;
  2481.     },
  2482.  
  2483.     inspectObject: function(object, context)
  2484.     {
  2485.         Firebug.chrome.select(object);
  2486.     },
  2487.  
  2488.     browseObject: function(object, context)
  2489.     {
  2490.     },
  2491.  
  2492.     persistObject: function(object, context)
  2493.     {
  2494.     },
  2495.  
  2496.     getRealObject: function(object, context)
  2497.     {
  2498.         return object;
  2499.     },
  2500.  
  2501.     getTitle: function(object)
  2502.     {
  2503.         var label = safeToString(object); // eg [object XPCWrappedNative [object foo]]
  2504.  
  2505.         const re =/\[object ([^\]]*)/;
  2506.         var m = re.exec(label);
  2507.         var n = null;
  2508.         if (m)
  2509.             n = re.exec(m[1]);  // eg XPCWrappedNative [object foo
  2510.  
  2511.         if (n)
  2512.             return n[1];  // eg foo
  2513.         else
  2514.             return m ? m[1] : label;
  2515.     },
  2516.  
  2517.     getTooltip: function(object)
  2518.     {
  2519.         return null;
  2520.     },
  2521.  
  2522.     /*
  2523.     * Called by chrome.onContextMenu to build the context menu when the underlying object has this rep.
  2524.     * See also Panel for a similar function also called by onContextMenu
  2525.     * Extensions may monkey patch and chain off this call
  2526.     * @param object: the 'realObject', a model value, eg a DOM property
  2527.     * @param target: the HTML element clicked on.
  2528.     * @param context: the context, probably FirebugContext
  2529.     * @return an array of menu items.
  2530.     */
  2531.     getContextMenuItems: function(object, target, context)
  2532.     {
  2533.         return [];
  2534.     },
  2535.  
  2536.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2537.     // Convenience for domplates
  2538.  
  2539.     STR: function(name)
  2540.     {
  2541.         return $STR(name);
  2542.     },
  2543.  
  2544.     cropString: function(text)
  2545.     {
  2546.         return cropString(text);
  2547.     },
  2548.  
  2549.     cropMultipleLines: function(text, limit)
  2550.     {
  2551.         return cropMultipleLines(text, limit);
  2552.     },
  2553.  
  2554.     toLowerCase: function(text)
  2555.     {
  2556.         return text ? text.toLowerCase() : text;
  2557.     },
  2558.  
  2559.     plural: function(n)
  2560.     {
  2561.         return n == 1 ? "" : "s";
  2562.     }
  2563. });
  2564.  
  2565. // ************************************************************************************************
  2566.  
  2567. /**
  2568.  * Implementation of ActivableModule.
  2569.  */
  2570. Firebug.ActivableModule = extend(Firebug.Module,
  2571. {
  2572.     panelName: null,
  2573.     panelBar1: $("fbPanelBar1"),
  2574.  
  2575.     initialize: function()
  2576.     {
  2577.         this.dependents = [];
  2578.         this.disabledPanelPage = new Firebug.DisabledPanelPage(this);
  2579.  
  2580.         Firebug.Module.initialize.apply(this, arguments);
  2581.     },
  2582.  
  2583.     initializeUI: function(detachArgs)
  2584.     {
  2585.         Firebug.registerUIListener(this);  // we listen for showUI/hideUI for panel activation
  2586.  
  2587.         this.updateTab(null);
  2588.     },
  2589.  
  2590.     shutdown: function()
  2591.     {
  2592.         Firebug.Module.shutdown.apply(this, arguments);
  2593.  
  2594.         Firebug.unregisterUIListener(this);
  2595.     },
  2596.  
  2597.     reattachContext: function(browser, context)
  2598.     {
  2599.         this.updateTab();
  2600.     },
  2601.  
  2602.     showContext: function(browser, context)
  2603.     {
  2604.         this.updateTab();
  2605.     },
  2606.  
  2607.     destroyContext: function(context)
  2608.     {
  2609.     },
  2610.  
  2611.     isEnabled: function()
  2612.     {
  2613.         return this.isAlwaysEnabled();
  2614.     },
  2615.  
  2616.     panelEnable: function(context) // panel Disabled -> Enabled for every context with a panel
  2617.     {
  2618.         var panel = context.getPanel(this.panelName, false);
  2619.         if (panel)
  2620.             panel.enablePanel(this);
  2621.  
  2622.         this.onEnabled(context);
  2623.     },
  2624.  
  2625.     panelDisable: function(context)  // panel Enabled -> Disabled for every context with a panel
  2626.     {
  2627.         var panel = context.getPanel(this.panelName, true);
  2628.         if (panel)
  2629.             panel.disablePanel(this);
  2630.  
  2631.         this.onDisabled(context);
  2632.     },
  2633.  
  2634.     onEnabled: function(context)
  2635.     {
  2636.         // called for each context at the end of enable
  2637.     },
  2638.  
  2639.     onDisabled: function(context)
  2640.     {
  2641.         // called for each context at the end of disable
  2642.     },
  2643.  
  2644.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2645.     // Cross module dependencies.
  2646.  
  2647.     addDependentModule: function(dependent)
  2648.     {
  2649.         this.dependents.push(dependent);
  2650.         this.onDependentModuleChange(dependent);  // not dispatched.
  2651.     },
  2652.  
  2653.     removeDependentModule: function(dependent)
  2654.     {
  2655.         remove(this.dependents, dependent);
  2656.         this.onDependentModuleChange(dependent);  // not dispatched
  2657.     },
  2658.  
  2659.     onDependentModuleChange: function(dependent)
  2660.     {
  2661.     },
  2662.  
  2663.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2664.     // uiListener
  2665.  
  2666.     showUI: function(browser, context)  // Firebug is opened, in browser or ed
  2667.     {
  2668.     },
  2669.  
  2670.     hideUI: function(browser, context)  // Firebug closes, either in browser or detached.
  2671.     {
  2672.     },
  2673.  
  2674.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2675.  
  2676.     onPanelEnable: function(panelName)
  2677.     {
  2678.         // Module activation code.
  2679.     },
  2680.  
  2681.     onPanelDisable: function(panelName)
  2682.     {
  2683.         // Module deactivation code.
  2684.     },
  2685.  
  2686.     onSuspendFirebug: function( )
  2687.     {
  2688.         // When the number of activeContexts decreases to zero. Modules should remove listeners, disable function that takes resources
  2689.     },
  2690.  
  2691.     onResumeFirebug: function( )
  2692.     {
  2693.         // When the number of activeContexts increases from zero. Modules should undo the work done in onSuspendFirebug
  2694.     },
  2695.  
  2696.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2697.  
  2698.     getPrefDomain: function()
  2699.     {
  2700.         if (!this.panelName)
  2701.             return null;
  2702.  
  2703.         if (!this.prefDomain)
  2704.             this.prefDomain = Firebug.prefDomain + "." + this.panelName;
  2705.  
  2706.         return this.prefDomain;
  2707.     },
  2708.  
  2709.     setDefaultState: function(enable)
  2710.     {
  2711.         var prefDomain = this.getPrefDomain();
  2712.         if (!prefDomain)
  2713.         {
  2714.             return;
  2715.         }
  2716.  
  2717.         Firebug.setPref(prefDomain, "enableSites", enable);
  2718.     },
  2719.  
  2720.     isAlwaysEnabled: function()
  2721.     {
  2722.         var prefDomain = this.getPrefDomain();
  2723.         if (!prefDomain)
  2724.             return false;
  2725.  
  2726.         return Firebug.getPref(prefDomain, "enableSites");
  2727.     },
  2728.  
  2729.     get enabled() // backward compat
  2730.     {
  2731.         return this.isAlwaysEnabled();
  2732.     },
  2733.  
  2734.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2735.  
  2736.     onEnablePrefChange: function(pref)
  2737.     {
  2738.         var panelPref = this.getPrefDomain()+".enableSites";
  2739.  
  2740.         if (pref == panelPref)
  2741.         {
  2742.             Firebug.ModuleManager.changeActivation(this);
  2743.         }
  2744.     },
  2745.  
  2746.     updateTab: function()
  2747.     {
  2748.         if (!this.panelName && (FBTrace.DBG_PANELS || FBTrace.DBG_ERRORS))
  2749.             FBTrace.sysout("firebug.ActivableModule.updateTab; Missing panelName in activable module", this);
  2750.  
  2751.         // Set activable module to mini tab menu so, the menu can get the actual state.
  2752.         var panelBar = Firebug.chrome.$("fbPanelBar1");
  2753.         var tab = panelBar.getTab(this.panelName);
  2754.         if (tab)
  2755.         {
  2756.             tab.setModule(this);
  2757.             var enabled = this.isAlwaysEnabled();
  2758.             if (enabled)
  2759.                 tab.setAttribute("aria-disabled", "false");
  2760.             else
  2761.                 tab.setAttribute("aria-disabled", "true");
  2762.         }
  2763.         else
  2764.         {
  2765.         }
  2766.  
  2767.     }
  2768. });
  2769.  
  2770. // ************************************************************************************************
  2771.  
  2772. Firebug.DisabledPanelPage = function(module)
  2773. {
  2774.     this.module = module;
  2775. }
  2776.  
  2777. Firebug.DisabledPanelPage.prototype = domplate(Firebug.Rep,
  2778. {
  2779.     tag:
  2780.         DIV({"class": "disabledPanelBox"},
  2781.             H1({"class": "disabledPanelHead"},
  2782.                 SPAN("$pageTitle")
  2783.             ),
  2784.             P({"class": "disabledPanelDescription", style: "margin-top: 15px;"},
  2785.                 $STR("moduleManager.desc4"),
  2786.                 SPAN(" "),
  2787.                 SPAN({"class": "descImage descImage-$panelName"})
  2788.             )
  2789.             /* need something here that pushes down any thing appended to the panel */
  2790.          ),
  2791.  
  2792.     getModuleName: function(module)
  2793.     {
  2794.         var panelType = Firebug.getPanelType(module.panelName);
  2795.         return Firebug.getPanelTitle(panelType);
  2796.     },
  2797.  
  2798.     onEnable: function(event)
  2799.     {
  2800.         Firebug.ModuleManager.enableModules(FirebugContext);
  2801.     },
  2802.  
  2803.     show: function(panel)
  2804.     {
  2805.         // Always render the page so, the previous content is properly replaced.
  2806.         //if (!panel.disabledBox)
  2807.             this.render(panel);
  2808.  
  2809.         panel.disabledBox.setAttribute("collapsed", false);
  2810.         panel.panelNode.scrollTop = 0;
  2811.  
  2812.     },
  2813.  
  2814.     hide: function(panel)
  2815.     {
  2816.         if (!panel.disabledBox)
  2817.             return;
  2818.  
  2819.         panel.disabledBox.setAttribute("collapsed", true);
  2820.     },
  2821.  
  2822.     render: function(panel)
  2823.     {
  2824.         // Prepare arguments for the template.
  2825.         var args = {
  2826.             pageTitle: $STRF("moduleManager.title", [this.getModuleName(this.module)]),
  2827.             panelName: this.module.panelName
  2828.         };
  2829.  
  2830.         // Render panel HTML
  2831.         panel.disabledBox = this.tag.replace(args, panel.panelNode, this);
  2832.         panel.panelNode.scrollTop = 0;
  2833.     }
  2834. });
  2835.  
  2836. // ************************************************************************************************
  2837.  
  2838. Firebug.ModuleManager =
  2839. {
  2840.     disableModules: function(context)
  2841.     {
  2842.         if (!context)
  2843.             context = FirebugContext;
  2844.  
  2845.         for (var i=0; i<activableModules.length; i++)
  2846.         {
  2847.             var module = activableModules[i];
  2848.             this.disableModule(module);
  2849.         }
  2850.     },
  2851.  
  2852.     enableModules: function(context)
  2853.     {
  2854.         for (var i=0; i<activableModules.length; i++)
  2855.         {
  2856.             var module = activableModules[i];
  2857.             this.enableModule(module);
  2858.         }
  2859.     },
  2860.  
  2861.     disableModule: function(module)
  2862.     {
  2863.         if (module.isAlwaysEnabled())  // if we are enabled,
  2864.             module.setDefaultState(false);  // change the pref, triggering disable
  2865.         else
  2866.             this.changeActivation(module); // pref is ok, just disable
  2867.     },
  2868.  
  2869.     enableModule: function(module)
  2870.     {
  2871.         if (!module.isAlwaysEnabled())
  2872.             module.setDefaultState(true);
  2873.         else
  2874.             this.changeActivation(module);
  2875.     },
  2876.  
  2877.     changeActivation: function(module)
  2878.     {
  2879.         if (module.isAlwaysEnabled())
  2880.             dispatch(modules, "onPanelEnable", [module.panelName]);
  2881.         else
  2882.             dispatch(modules, "onPanelDisable", [module.panelName]);
  2883.  
  2884.         module.updateTab();
  2885.         Firebug.resetTooltip();
  2886.  
  2887.         TabWatcher.iterateContexts(
  2888.             function changeActivation(context)
  2889.             {
  2890.                 try
  2891.                 {
  2892.                     if (module.isAlwaysEnabled())
  2893.                         module.panelEnable(context);
  2894.                     else
  2895.                         module.panelDisable(context);
  2896.                 }
  2897.                 catch (exc)
  2898.                 {
  2899.                 }
  2900.             }
  2901.         );
  2902.  
  2903.     },
  2904.  
  2905.     obeyPrefs: function(context)
  2906.     {
  2907.         for (var i=0; i<activableModules.length; i++)
  2908.         {
  2909.             var module = activableModules[i];
  2910.             if (module.isAlwaysEnabled())
  2911.                 this.enableModule(module);
  2912.             else
  2913.                 this.disableModule(module);
  2914.  
  2915.             module.updateTab();
  2916.         }
  2917.     },
  2918. }
  2919.  
  2920. // ************************************************************************************************
  2921. /*
  2922.  * If we are detached and the main Firefox window closes, also close the matching Firebug window.
  2923.  */
  2924. function shutdownFirebug() {
  2925.     try
  2926.     {
  2927.         if (Firebug.isDetached())
  2928.             Firebug.chrome.close();
  2929.     }
  2930.     catch (exc)
  2931.     {
  2932.         window.dump("shutdownFirebug FAILS: "+exc+"\n");
  2933.     }
  2934.  
  2935.     Firebug.shutdown();
  2936. }
  2937.  
  2938. }});
  2939.